import * as THREE from "three";

// 引入轨道控制器控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 引入动画库
import gsap from "gsap";
// 引入数据图形用户界面库
import * as dat from "dat.gui";
// 引入rgbeLoad
// DataTextureLoader
// 用于加载二进制文件格式的(rgbe, hdr, ...)的抽象类。 内部使用FileLoader来加载文件， 和创建一个新的 DataTexture.
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"
// 引入物理引擎
import * as CANNON from 'cannon-es';
// 用于载入glTF 2.0资源的加载器。
// glTF（gl传输格式）是一种开放格式的规范 （open format specification）， 用于更高效地传输、加载3D内容。
// 该类文件以JSON（.gltf）格式或二进制（.glb）格式提供， 外部文件存储贴图（.jpg、.png）和额外的二进制数据（.bin）。
// 一个glTF组件可传输一个或多个场景， 包括网格、材质、贴图、蒙皮、骨架、变形目标、动画、灯光以及摄像机。
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"

// 导入顶点着色器与片元着色器
import PointsBasicVertexShader from "../shader/pointsBasic/PointBasicVertexShader.glsl";
import PointsBasicFragmentShader from "../shader/pointsBasic/PointBasicFragmentShader.glsl";


// 导入官方例子中使用的水实现 需要导入材质纹理
import { Water } from 'three/examples/jsm/objects/Water2.js';


//three.js 基本内容

// 1.创建场景  场景能够让你在什么地方、摆放什么东西来交给three.js来渲染，这是你放置物体、灯光和摄像机的地方。
const scene = new THREE.Scene();

// 2.创建相机 
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 300);

//2.1设置相机的位置 position表示对象局部位置的Vector3。默认值为(0, 0, 0)。
camera.position.set(0, 0, 2);
scene.add(camera); //添加相机到场景中

// 生成点
// const geometry  = new THREE.BufferGeometry();
// const positions = new Float32Array([0,0,0]);
// geometry.setAttribute("position",new THREE.BufferAttribute(positions,3));

// 点材质
// const material = new THREE.PointsMaterial({
//     color:0xffffff,
//     size:10,
//     sizeAttenuation:true
// });
// 导入材质纹理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('texture/particles/10.png');
const texture1 = textureLoader.load('texture/particles/9.png');
const texture2 = textureLoader.load('texture/particles/11.png');

const params = {
    count: 2000,
    size: 0.4,
    radius: 6,
    branch: 4,
    color: "#ff6030",
    rotateScale: 0.3,
    endColor: "#1b3984",
}
const centerColor = new THREE.Color(params.color);
const endColor = new THREE.Color(params.endColor);
let [geometry, material, points] = [null, null, null];
material = new THREE.ShaderMaterial({
    vertexShader: PointsBasicVertexShader,
    fragmentShader: PointsBasicFragmentShader,
    transparent: true,
    sizeAttenuation: true,
    depthWrite: false,
    blending: THREE.AdditiveBlending,
    uniforms: {
        uTexture: {
            value: texture
        },
        uTexture1: {
            value: texture1
        },
        uTexture2: {
            value: texture2
        },
        uTime:{
            value:0
        }
    }
});
const generateGalaxy = () => {
    // 生成顶点
    geometry = new THREE.BufferGeometry();
    // 生成随机位置
    const positions = new Float32Array(params.count * 3);
    // 设置顶点颜色
    const colors = new Float32Array(params.count * 3);

    // 设置顶点材质贴图
    const imgIndex = new Float32Array(params.count);
    // 设置顶点大小
    const aScale = new Float32Array(params.count);

    // 循环生成顶点、
    for (let i = 0; i < params.count; i++) {

        let current = i * 3;
        //   当前的点应该在哪一条分支的角度上
        const branchAngel = (i % params.branch) * ((2 * Math.PI) / params.branch);

        const radius = Math.random() * params.radius;

        // 当前点距离圆心的距离
        const distance = Math.random() * params.radius * Math.pow(Math.random(), 3);


        // const randomX = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance))  / 6;
        // const randomY = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 6 ;
        // const randomZ = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance))  / 6;

        // positions[current] = Math.cos(branchAngel + distance * params.rotateScale) * distance + randomX;
        // positions[current + 1] = 0 + randomY;
        // positions[current + 2] = Math.sin(branchAngel + distance * params.rotateScale) * distance + randomZ;

        // 随机设置x/y/z偏移值
        const randomX =
            Math.pow(Math.random() * 2 - 1, 3) * 0.5 * (params.radius - radius) * 0.3;
        const randomY =
            Math.pow(Math.random() * 2 - 1, 3) * 0.5 * (params.radius - radius) * 0.3;
        const randomZ =
            Math.pow(Math.random() * 2 - 1, 3) * 0.5 * (params.radius - radius) * 0.3;

        // 设置当前点x值坐标
        positions[current] = Math.cos(branchAngel) * radius + randomX;
        // 设置当前点y值坐标
        positions[current + 1] = randomY;
        // 设置当前点z值坐标
        positions[current + 2] = Math.sin(branchAngel) * radius + randomZ;



        // 混合颜色，形成渐变色
        const mixColor = centerColor.clone();
        mixColor.lerp(endColor, distance / params.radius);

        colors[current] = mixColor.r;
        colors[current + 1] = mixColor.g;
        colors[current + 2] = mixColor.b;

        imgIndex[i] = Math.round(Math.random() * 3);
        aScale[i] = Math.random();
    }

    // console.log(aScale)
    geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
    // 设置自定义属性
    geometry.setAttribute("imgIndex", new THREE.BufferAttribute(imgIndex, 1));
    geometry.setAttribute("aScale", new THREE.BufferAttribute(aScale, 1));


    // material = new THREE.PointsMaterial({
    //     // size: params.size,
    //     color: new THREE.Color(params.color),
    //     sizeAttenuation: true,
    //     depthWrite: false,
    //     blending: THREE.AdditiveBlending,
    //     map: sprite,
    //     alphaMap: sprite,
    //     transparent: true,
    //     vertexColors: true,
    // });
    points = new THREE.Points(geometry, material);
    scene.add(points);
}
generateGalaxy();




// const points = new THREE.Points(geometry,material);
// scene.add(points);

// 4.渲染
// 初始化渲染器
// WebGLRenderer WebGL Render 用WebGL渲染出你精心制作的场景。
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// .domElement : DOMElement   一个canvas，渲染器在其上绘制输出。
// 渲染器的构造函数会自动创建(如果没有传入canvas参数);你需要做的仅仅是像下面这样将它加页面里去:
document.body.appendChild(renderer.domElement); //将绘制canvas添加到页面里
// 开启场景中的阴影贴图
// renderer.shadowMap.enabled = true;
// renderer.physicallyCorrectLights = true; // 新版本不用设置 物理正确光源 decay也会生效。 老版本开阴影要设置


//创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

// 创建坐标轴辅助器
// AxesHelper  用于简单模拟3个坐标轴的对象.
// 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
// AxesHelper( size : Number )  size -- (可选的) 表示代表轴的线段长度. 默认为 1.
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

// 设置时钟
const clock = new THREE.Clock();


function render() {
    let elapsedTime = clock.getElapsedTime();
    material.uniforms.uTime.value = elapsedTime;
    controls.update();
    renderer.render(scene, camera);
    // requestAnimationFrame 是一个用于优化浏览器动画效果的 API。它可以让浏览器在下一次重绘前执行指定的回调函数，
    // 从而可以更加流畅地执行动画效果，避免了使用 setTimeout 或 setInterval 可能引起的性能问题。
    requestAnimationFrame(render);
}

render();

// 窗口变化时，更新渲染画面
window.addEventListener("resize", () => {

    // 更新摄像机视锥体长宽比
    camera.aspect = window.innerWidth / window.innerHeight;
    // 更新摄像机投影矩阵。在任何参数被改变以后必须被调用
    camera.updateProjectionMatrix();

    // 更新渲染器
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 设置渲染器像素比 通常用于避免HiDPI设备上绘图模糊
    renderer.setPixelRatio(window.devicePixelRatio);

})


